package cn.com.spring;

import cn.com.spring.annotation.Async;
import cn.com.spring.annotation.BeanScan;
import cn.com.utils.AopUtil;
import cn.com.utils.ClassScannerUtil;
import cn.com.utils.FileUtil;
import cn.com.utils.StringUtil;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 豆制品厂
 *
 * @author LiuGuodong
 * @date 2023/10/07
 */
public class BeanFactory {

    private static final Map<String, Object> BEAN_MAP = new ConcurrentHashMap<>();
    private static final Map<String, Object> ASPECT_MAP = new ConcurrentHashMap<>();
    public static final String PATH = FileUtil.getEnv("path");

    public BeanFactory() {

    }

    public BeanFactory(Class<?> mainClass) {
        init(mainClass);
    }

    public static void init(Class<?> mainClass) {
        BeanScan beanScan = mainClass.getAnnotation(BeanScan.class);
        List<Class> classList = scanClasses(beanScan);

        classList.forEach(aClass -> {
            if (!Modifier.isInterface(aClass.getModifiers())) {
                createAndSaveBean(aClass);
            }
        });
    }

    private static List<Class> scanClasses(BeanScan beanScan) {
        String scanPath = Objects.nonNull(beanScan) && StringUtil.isNotBlank(beanScan.value()) ? beanScan.value() : PATH;
        return ClassScannerUtil.scanClassesFile(scanPath);
    }

    private static void createAndSaveBean(Class<?> aClass) {
        try {
            Object bean = aClass.getDeclaredConstructor().newInstance();
            Object processed = processAsyncAnnotation(aClass);
            if (Objects.nonNull(processed)) {
                bean = processed;
            }
            saveToFactory(aClass, bean);
        } catch (InstantiationException | IllegalAccessException | NoSuchMethodException |
                 InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    private static Object processAsyncAnnotation(Class<?> aClass) {
        for (Method method : aClass.getMethods()) {
            Async annotation = method.getAnnotation(Async.class);
            if (annotation != null) {
                try {
                    return AopUtil.getAsyncProxy(aClass);
                } catch (InstantiationException | IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return null;
    }

    public static Object getBean(String key) {
        return BEAN_MAP.getOrDefault(key, null);
    }

    public static <T> T getBean(Class<T> tClass) {
        return (T) BEAN_MAP.getOrDefault(tClass.getName(), null);
    }

    public static Object getAspect(Class tClass) {
        return ASPECT_MAP.getOrDefault(tClass.getName(), null);
    }

    /**
     * 设置方面
     * 存放切面逻辑
     *
     * @param key  切面的注解
     * @param bean 切面类的实例
     */
    public static void setAspect(String key, Object bean) {
        if (Objects.isNull(key) || Objects.isNull(bean)) {
            return;
        }
        ASPECT_MAP.put(key, bean);
    }


    public static void setBean(String key, Object bean) {
        if (Objects.isNull(key) || Objects.isNull(bean)) {
            return;
        }
        BEAN_MAP.put(key, bean);
    }

    public static void saveToFactory(Class<?> aClass, Object targetObject) {
        setBean(aClass.getSimpleName(), targetObject);
        setBean(aClass.getName(), targetObject);

        Class<?>[] interfaces = aClass.getInterfaces();
        for (Class<?> anInterface : interfaces) {
            setBean(anInterface.getSimpleName(), targetObject);
            setBean(anInterface.getName(), targetObject);
        }
    }

    public static void setBeanMap(Map<String, Object> beans) {
        BEAN_MAP.putAll(beans);
    }

    public static <T> T getProxyBean(Class<T> tClass) throws Exception {
        return AopUtil.getCurrencyProxy(tClass);
    }


}
